<?php if (!defined('PmWiki')) exit();

/**	=== Attachtable FileInfo ===
 *	Copyright 2009 Eemeli Aro <eemeli@gmail.com>
 */

if ( !IsEnabled($EnableUpload,0) ) return;

SDVA( $HandleAuth, array(
	'fileinfo' => $HandleAuth['download'],
	'delfileinfo' => $HandleAuth['downloaddeleted']
) );
SDV( $HandleAuth['thumbnail'], $HandleAuth['fileinfo'] );

SDVA( $HandleActions, array(
	'fileinfo' => 'HandleFileInfo',
	'thumbnail' => 'HandleFileInfoThumbnail',
) );

SDV($LinkFunctions['Thumbnail:'], 'LinkThumbnail');
SDV($IMap['Thumbnail:'], '$1');
Markup( 'thumbnail','<img',
  "/\\b(?>(Thumbnail:))([^\\s$UrlExcludeChars]*[^\\s.,?!$UrlExcludeChars])(\"([^\"]*)\")?/e",
  "Keep(LinkThumbnail(\$pagename,'$1','$2','$4','$1$2', \$GLOBALS['ImgTagFmt']),'L')" );


include_once('attachtable-util.php');


function FileInfo_Abort( $msg, $status='200 OK' ) {
	global $FileInfoID, $Charset;
	header( "HTTP/1.1 $status");
	header( "Content-type: text/plain; charset=$Charset" );
	echo @$FileInfoID . preg_replace( '/\\$\\[([^\\]]+)\\]/e', "XL(PSS('$1'))", $msg );
	exit();
}

function FileInfoVerify( &$pagename, &$auth ) {
	global
		$EnableIMSCaching, $HTTPHeaders, $HandleAuth, $UploadFileFmt,
		$FileInfoID;

	$upname = @$_REQUEST['upname'];
	if (preg_match( '!^(.*)/([^/]+)$!', $upname, $match )) {
		$pagename = MakePageName( $pagename, $match[1] );
		$upname = $match[2];
	}

	$FileInfoID = str_replace( ',', ':', "$pagename:$upname\n" );

	if ($upname=='') FileInfo_Abort( 'no attachment name', '400 Bad Request' );
	if (preg_match( '/^(.*?),(\d+)$/', $upname, $delmatch )) {
		$auth = $HandleAuth['delfileinfo'];
		$upname = MakeUploadName( $pagename, $delmatch[1] ).','.$delmatch[2];
	} else {
		$upname = MakeUploadName( $pagename, $upname );
	}

	if ( !RetrieveAuthPage( $pagename, $auth, FALSE, READPAGE_CURRENT ) ) FileInfo_Abort( 'insufficient authority', '401 Unauthorized' );

	$filepath = FmtPageName( "$UploadFileFmt/$upname", $pagename );
	if ( !file_exists($filepath) ) FileInfo_Abort( "no such attachment: $upname", '404 Not Found' );
	if ( is_dir($filepath) ) FileInfo_Abort( "$upname is a directory", '400 Bad Request' );

	if (!empty( $EnableIMSCaching )) {
		foreach ( $HTTPHeaders as $k => $h ) if (preg_match( '/^(Expires|Cache-Control):/', $h )) unset($HTTPHeaders[$k]);
		header("Cache-Control: ");
		header("Expires: ");
		$filelastmod = gmdate( 'D, d M Y H:i:s \G\M\T', filemtime($filepath) );
		if ( @$_SERVER['HTTP_IF_MODIFIED_SINCE'] == $filelastmod ) {
			header("HTTP/1.0 304 Not Modified");
			exit(0);
		}
		header("Last-Modified: $filelastmod");
	}

	return array( $upname, $filepath );
}

function LinkThumbnail( $pagename, $imap, $path, $alt, $txt, $fmt=NULL ) {
	global
		$FmtV, $UploadFileFmt, $ImgExtPattern,
		$LinkMissingThumbnailFmt;

	SDV( $LinkMissingThumbnailFmt, '$[No thumbnail]' );

	if (preg_match( "/^(.*)&amp;fmt=$ImgExtPattern$/", $path, $match )) $path = $match[1];
	if (preg_match('!^(.*)/([^/]+)$!', $path, $match)) {
		$pagename = MakePageName($pagename, $match[1]);
		$path = $match[2];
	}
	$upname = MakeUploadName($pagename, $path);
	$filepath = FmtPageName("$UploadFileFmt/$upname", $pagename);
	$FmtV['$LinkText'] = $txt;
	if (!file_exists($filepath)) return FmtPageName( $LinkMissingThumbnailFmt, $pagename);
	$path = PUE(FmtPageName( "{\$PageUrl}?action=thumbnail&amp;upname=$upname", $pagename ));
	return LinkIMap($pagename, $imap, $path, $alt, $txt, $fmt);
}

function FileInfoThumbnailLink( $pagename, $upname, $filepath, $exif_tn=-1 ) {
	global $ImgExtPattern, $FileInfoThumbnailFmt;

	if ( $exif_tn < 0 ) {
		$tn_img = is_callable('exif_thumbnail')
			? exif_thumbnail( $filepath, $width, $height, $type)
			: FALSE;
		$exif_tn = $tn_img ? $type : 0;
	}
	if ($exif_tn==2) {
		$fn = "$pagename/$upname";
		if (!preg_match( "/$ImgExtPattern$/", $fn )) $fn .= '&fmt=.jpg'; //. ( ($exif_tn==2) ? 'jpg' : 'tiff' );
		return "Thumbnail:$fn";
	}

	if (!empty( $FileInfoThumbnailFmt )) foreach( $FileInfoThumbnailFmt as $pat => $rep ) {
		$tn_pat = preg_replace( $pat, $rep, $upname, -1, $count );
		if ($count) {
			if ( $tn_pat[0] == '/' ) {
				if ( $dirp = @opendir(dirname($filepath)) ) {
					while ( ( $file = readdir($dirp) ) !== FALSE ) if (preg_match( $tn_pat, $file )) {
						closedir($dirp);
						return "Attach:$pagename/$file";
					}
					closedir($dirp);
				}
			} else if (file_exists( dirname($filepath)."/$tn_pat" )) {
				return "Attach:$pagename/$tn_pat";
			}
		}
	}

	return '';
}

function HandleFileInfoThumbnail( $pagename, $auth = 'read' ) {
	list( $upname, $filepath ) = FileInfoVerify( $pagename, $auth );

	if (is_callable( 'exif_thumbnail' )) $img = exif_thumbnail( $filepath, $width, $height, $type);
	if (empty($img)) FileInfo_Abort( 'no thumbnail available', '404 Not Found' );
	header( 'Content-type: '.image_type_to_mime_type($type) );
	header( 'Content-Length: '.strlen($img) );
	echo $img;
	exit(0);
}

function FileInfoGetStat( $pagename, $upname, $filepath, &$out ) {
	global $TimeFmt, $UploadExts;
	if ( $stat = stat($filepath) ) {
		$out[] = "|| ||\n||'''$[File]:''' ||";
		$out['mime'] = '||$[MIME type]: ||' . AttachFiletype( $filepath, TRUE ) . ' ||';
		$out[] = '||$[Size (bytes)]: ||' . AttachFilesizeString( $stat['size'], TRUE ) . ' ||';
		//$out[] = '||$[Accessed]: ||' . strftime( $TimeFmt, $stat['atime'] ) . ' ||';
		$out[] = '||$[Modified]: ||' . strftime( $TimeFmt, $stat['mtime'] ) . ' ||';
		$out[] = '||$[Inode changed]: ||' . strftime( $TimeFmt, $stat['ctime'] ) . ' ||';
	}
}

function FileInfoGetRecentChanges( $pagename, $upname, $filepath, &$out ) {
	global $FileInfoLogFmt;
	if (empty( $FileInfoLogFmt )) return;
	foreach( $FileInfoLogFmt as $rcfmt => $patfmt) {
		$rcname = FmtPageName( $rcfmt, $pagename );  if (!$rcname) continue;
		$pat = str_replace( '$upname', preg_quote($upname,'/'), $patfmt, $count ); if (!$count) continue;
		$rcpage = RetrieveAuthPage( $rcname, 'read', FALSE, READPAGE_CURRENT ); if ( !$rcpage || empty($rcpage['text']) ) continue;
		preg_match_all( "/^$pat$/m", $rcpage['text'], $matches, PREG_SET_ORDER ); if (!$matches) continue;
		$out[] = "|| ||\n||'''$[Log]:''' ||";
		foreach( array_reverse($matches) as $m ) $out[] = "||{$m[1]} ||{$m[2]} ||";
		break;
	}
}

function FileInfoGetID3( $pagename, $upname, $filepath, &$out ) {
	global $FileInfoShowErrors;

	if (!file_exists( dirname(__FILE__).'/getid3/getid3.php' )) return;

	include_once('getid3/getid3.php');
	$getID3 = new getID3;
	$info = $getID3->analyze($filepath);

	if ( !empty($info['error'])
		&& in_array( 'unable to determine file format', $info['error'] )
	) return;

	if (!empty( $info['mime_type'] )) $out['mime'] = "||$[MIME type]: ||{$info['mime_type']} ||";

	if (!empty( $info['playtime_seconds'] )) {
		$t = floatval($info['playtime_seconds']);
		if ( $t > 0.0 ) {
			$ph = floor( $t/3600 );
			$pm = floor( $t/60 ) - 60*$ph;
			$out[] = "|| ||\n||$[Playtime]: ||"
				. ( $ph ? "$ph:" : '' )
				. "$pm:"
				. sprintf( '%06.3f', fmod( $t, 60 ) )
				. ' ||';
		}
	}

	if ( IsEnabled( $FileInfoShowErrors, 0 )) foreach( array('error','warning') as $t )
		if (!empty( $info[$t] )) foreach ( $info[$t] as $e )
			$out[] = "|| ||\n||'''$[$t]:''' ||'''$e''' ||";

	$img = preg_match( '/^(bmp|gif|jpg|pcd|png|svg|tiff)$/', $info['fileformat'] );

	$av = array(
		'audio' => array( 'sample_rate'=>'', 'bitrate'=>'', 'channels'=>'' ),
		'video' => array( 'pixel_size'=>'', 'frame_rate'=>'' )
	);
	foreach( array('audio','video') as $h ) if (!empty($info[$h])) {
		$avs =& $info[$h];
		if (!empty( $avs['bitrate'] )) {
			$bm = (@$avs['bitrate_mode']=='vbr') ? ' (variable)' : '';
			$av[$h]['bitrate'] = sprintf( '%.1f', $avs['bitrate']/1024 ) . " kbps$bm";
		}
		foreach( array('codec','dataformat','encoder') as $t ) if (!empty( $avs[$t] )) $av[$h][$t] = $avs[$t];
		if (!empty( $avs['compression_ratio'] )) $av[$h]['compression'] = sprintf( '%.2f%%', 100*(1-$avs['compression_ratio']) );
		if (isset( $avs['lossless'] )) $av[$h]['lossless'] = $avs['lossless'] ? 'true' : 'false';
	}

	if (!empty( $info['audio']['channels'] )) {
		$cm = empty($info['audio']['channelmode']) ? '' : " ({$info['audio']['channelmode']})";
		$av['audio']['channels'] = $info['audio']['channels'] . $cm;
	}
	if (!empty( $info['audio']['sample_rate'] )) {
		$bps = empty($info['audio']['bits_per_sample']) ? '' : " @ {$info['audio']['bits_per_sample']} bits/sample";
		$av['audio']['sample_rate'] = $info['audio']['sample_rate']/1000 . ' kHz' . $bps;
	}

	if (!empty( $info['video']['resolution_x'] )) {
		$bps = empty($info['video']['bits_per_sample']) ? '' : " @ {$info['video']['bits_per_sample']} bits/px";
		$av['video']['pixel_size'] = $info['video']['resolution_x'] . ' &times; ' . @$info['video']['resolution_y'] . $bps;
	}
	if (!empty( $info['video']['frame_rate'] ))
		$av['video']['frame_rate'] = $info['video']['frame_rate'] . ' fps';
	if ( !empty($info['video']['pixel_aspect_ratio']) && ( $info['video']['pixel_aspect_ratio'] != 1.0 ) )
		$av['video']['pixel_aspect_ratio'] = $info['video']['pixel_aspect_ratio'];

	foreach( $av as $k => $a ) if (!empty($info[$k])) {
		$out[$k] = "|| ||\n||'''$["
			. ( $k=='audio' ? 'Audio' : ($img?'Image':'Video') )
			. "]:''' ||";
		foreach( $a as $kk => $v ) if (!empty($v))
			$out["$k.$kk"] = '||$[' . ucfirst(str_replace('_',' ',$kk)) . "]: ||$v ||";
	}

	if ( $img && file_exists( dirname(__FILE__).'/fileinfo-images.php' ) ) {
		include_once('fileinfo-images.php');

		if (!empty( $info[$info['fileformat']] )) switch($info['fileformat']) {
			case 'bmp':		FileInfoBMP( $info['bmp'], $out );		break;
			case 'gif':		FileInfoGIF( $info['gif'], $out, $filepath, $info['video']['compression_ratio'] );	break;
			case 'png':		FileInfoPNG( $info['png'], $out );		break;
			case 'tiff':	FileInfoTIFF( $info['tiff'], $out );	break;
			case 'jpg':
				if (!empty( $info['jpg']['exif'] )) FileInfoExifData( $info['jpg']['exif'], $out );
				$exif_tn = empty($info['jpg']['exif']['THUMBNAIL']) ? 0 : 2;
				if (!empty( $info['iptc'] )) foreach( $info['iptc'] as $rec => $rec_a ) foreach ( $rec_a as $itag => $tv_a )
					$out[] = "||$[$rec].$[$itag] ||".implode(",\\\\\n",$tv_a).' ||';
				break;
		}
	}
	if (!empty( $info['video'] )) $thumb = FileInfoThumbnailLink( $pagename, $upname, $filepath, $exif_tn );
	if (@$thumb) $out['thumb'] = "[[Attach:$pagename/$upname|$thumb]]\\\\";	// HACK

	if (!empty( $info['tags_html'] )) {
		$out[] = "|| ||\n||'''$[Tags]:''' ||";
		foreach ( $info['tags_html'] as $ta ) foreach ( $ta as $tk => $tv_a )
			$out[] = '||$['.ucfirst(str_replace('_',' ',$tk)).']: ||'.implode( ",\\\\\n", str_replace("\n",' ',$tv_a) ).' ||';
	}

	if (!empty( $info['comments'] )) {
		$out[] = "|| ||\n||'''$[Comments]:''' ||";
		foreach ( $info['comments'] as $ct => $ca )
			$out[] = '||$['.ucfirst(str_replace('_',' ',$ct)).']: ||'.implode( ",\\\\\n", str_replace("\n",' ',$ca) ).' ||';
	}

	if ( FALSE && $info ) {
		unset( $info['GETID3_VERSION'], $info['fileformat'], $info['mime_type'], $info['filename'], $info['filepath'],
			$info['filenamepath'], $info['avdataoffset'], $info['avdataend'], $info['filesize'], $info['error'], $info['warning'] );
		$out[] = '[@'.print_r($info,1).'@]';
	}
}

function HandleFileInfo( $pagename, $auth = 'read' ) {
	global
		$PageStartFmt, $PageEndFmt, $TmplFmt,
		$FileInfoID, $FileInfoFmt, $HandleFileInfoFmt, $FileInfoFixTitle;

	list( $upname, $filepath ) = FileInfoVerify( $pagename, $auth );

	$out = array();
	if (strpos( $upname, ',' )) $out['name'] = "'''[[$pagename?action=downloaddeleted&upname=".urlencode($upname)."|$upname]]'''\\\\";
	else $out['name'] = "'''[[Attach:$pagename/$upname|$upname]]'''\\\\";
	$out['thumb'] = '';
	$out['ext'] = ( ini_get('safe_mode') || !function_exists('exec') ) ? '' : trim(@exec( 'file -b ' . escapeshellarg($filepath) ));

	FileInfoGetStat( $pagename, $upname, $filepath, $out );
	FileInfoGetRecentChanges( $pagename, $upname, $filepath, $out );
	FileInfoGetID3( $pagename, $upname, $filepath, $out );

	if (empty( $out['thumb'] )) unset( $out['thumb'] );

	SDV( $FileInfoFmt, MarkupToHTML( $pagename, str_replace('<','&lt;',implode( "\n", $out )) ) );
	if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'XMLHTTP' ) !== FALSE ) {
		SDV( $HandleFileInfoFmt, array( $FileInfoID, &$FileInfoFmt ) );
	} else {
		SDV( $HandleFileInfoFmt, array( &$PageStartFmt, &$FileInfoFmt, &$PageEndFmt ) );
		if (IsEnabled( $FileInfoFixTitle, 1 )) $TmplFmt['Start'] = str_replace( array('{$Titlespaced}','$Titlespaced','{$Title}','$Title'), $upname, $TmplFmt['Start'] );
	}
	PrintFmt( $pagename, $HandleFileInfoFmt );
}
